from tools.send_to_ui_tools import *
from tools.save_image_to_device import *
from tools import common_tools
from db_tools.db_handler import DbHandler
import threading
import gevent
from tools.common_tools import *
from vision_process.image_process import find_total_board_info, get_image_used, find_total_board_info_6pin
from tools.send_to_ui_tools import *
from tools.config_params import ConfigParams

db = DbHandler()
logger = HdLogger()
cp = ConfigParams()

import time
import cv2 as cv
import socket
addr = ("192.168.0.3",1004)


"""
作业模块,主要负责自动作业的逻辑部分
"""


def _async_raise(tid, exctype):
    """raises the exception, performs cleanup if needed"""
    tid = ctypes.c_long(tid)
    if not inspect.isclass(exctype):
        exctype = type(exctype)
    res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))
    if res == 0:
        raise ValueError("invalid thread id")
    elif res != 1:
        # """if it returns a number greater than one, you're in trouble,
        # and you should call it again with exc=NULL to revert the effect"""
        ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)
        raise SystemError("PyThreadState_SetAsyncExc failed")


def stop_thread(thread):
    _async_raise(thread.ident, SystemExit)


class ProduceHandler(object):
    """
    生产作业相关的类,这个类主要用来正常的生产作业使用
    """

    def __init__(self, mainZmc, camera,deviceStates):
        self.mainZmc = mainZmc
        self.camera = camera
        self.deviceStates = deviceStates
        self.logger = logger
        self.boardIndex = 0
        self.clientSocket = None

    @staticmethod
    def web_request_login(userInfo):
        """
        前端界面点击登录按钮,登录界面
        :return:
        """
        username = userInfo.get("username", "")
        password = userInfo.get("password", "")

        if username == 'admin' and password == 'hengdingNo.1':
            res = {
                'currentAuthority': 'admin',
                'status': 'ok',
                'type': 'account'
            }
            db.update_login_state(True)
            return res
        userInfo = db.get_user_list_by_username(username)
        if len(userInfo) == 0:
            res = {
                'status': 'error',
                'errorMsg': '你输入的用户名不存在,请重新输入'
            }
            return res
        elif username == "" and password == "":
            res = {
                'status': 'error',
                'errorMsg': '输入的用户名和密码不能是空,请重新输入'
            }
            return res
        elif username != userInfo.get("username", "") or password != userInfo.get("password", ""):
            res = {
                'currentAuthority': 'guest',
                'status': 'error',
                'errorMsg': '你输入的密码错误,请重新输入',
                'type': 'account'
            }
            return res
        else:
            if userInfo.get("role", "") == "普通用户":
                res = {
                    'currentAuthority': "user",
                    'status': "ok",
                    'type': "account"
                }
            else:
                res = {
                    'currentAuthority': "serviceman",
                    'status': "ok",
                    'type': "account"
                }

            # 登录成功,刷新登录时间.
            query = {
                "username": username
            }
            tableName = "userinfo"
            newValues = {
                "$set": {
                    "lastLoginTime": common_tools.get_time_now_string()
                }
            }
            db.update_data(tableName, query, newValues)

            # 更新登录状态
            db.update_login_state(True)
            return res

    def keep_error_msg(self, errorMsg, errorType,boardIndex):
        if errorType == 'mainZmc':
            errorMsg = "主控制器通信异常: " + errorMsg
        else:
            errorMsg = '其他未归类错误'
        msgVal = {
            "val":"errorState",
            "errorInfo":errorMsg
        }
        self.logger.debug("id_{} {}".format(boardIndex,errorMsg))
        send_produce_state_to_ui(msgVal)


    # 相机作业监听线程.
    def camera_listener(self):
        self.logger.debug("相机监听作业函数开始...")
        self.lastCatchPos = None # 记录上次的抓板位置
        self.lastCatchAngle = None # 记录上次的抓板角度
        self.lastCatchTime = None # 上次的抓板时间,用来记录节拍
        # 先更新下自动作业的开始时间
        newData = {
            "key":self.key,
            "workAmount": 0,
            "workBegin": get_time_now_string_for_web(),
            "workEnd": get_time_now_string_for_web()
        }
        db.web_request_save_order(newData) # 直接更新工单
        msgVal = {
            "val": self.key
        }
        send_order_state_to_ui(msgVal)
        while True:
            if self.deviceStates.workStopFlag:
                self.logger.debug("检测到 wokStopFlag = True, 相机监听作业函数停止!")
                return

            # # 一直监听bit位,是否允许拍照
            # ret,val = self.mainZmc.get_allow_take_pic()
            # if ret != 0:
            #     self.keep_error_msg("获取 modbus_bit(15) 状态失败","mainZmc",self.boardIndex)
            #     return
            # if val == 1:
            #     # 进入到拍照的逻辑
            #     self.mainZmc.set_allow_tabke_pic_clear()
            #     self.logger.debug("id_{} 收到拍照信号,相机开始拍照".format(self.boardIndex))
            #     self.camera.start_grabbing()
            #     res, imageColor = self.camera.get_image()
            #     if res != 0:
            #         for i in range(3):
            #             res,imageColor = self.camera.get_image()
            #             if res == "ok":
            #                 break
            #         else:
            #             self.keep_error_msg("id_{} 相机拍照失败三次,请检查电源线和网线!","mainZmc",self.boardIndex)
            #             return
            #     image = get_image_used(imageColor)
            #     if self.jointNumber == "4":
            #         res, info, catchPos, angle, imageRes = find_total_board_info(image)
            #     else:
            #         res,info,catchPos,angle,imageRes = find_total_board_info_6pin(image,self.widthPix,self.heightPix)
            #     if res == "ok":
            #         if self.lastCatchTime is not None:
            #             catchBetween = time.time() - self.lastCatchTime
            #         else:
            #             catchBetween = 0
            #         self.logger.debug("id_{} {}拼板 识别成功,两次识别间隔: {:.2f},实际的抓板位置: {},角度:{}".format(
            #             self.boardIndex, self.jointNumber,catchBetween,catchPos,angle))
            #         print(catchPos, angle)
            #         catchPosToZmc,angleToZmc = self.get_send_data_to_zmc(catchPos,angle)
            #         print(catchPosToZmc, angleToZmc)
            #         self.lastCatchPos = catchPos
            #         self.lastCatchAngle = angle
            #         self.lastCatchTime = time.time()
            #         sendData = "1,{0:0<8},{1:0<8},{2:0<8},0,\r\n".format(
            #             float(catchPosToZmc[0]),float(catchPosToZmc[1]), float(angleToZmc))
            #         print(sendData)
            #         for i in range(1):
            #             self.clientSocket.send(sendData.encode("gbk"))
            #         self.logger.debug("id_{} 发送给底层的数据,{}".format(
            #             self.boardIndex, sendData))
            #         ret = self.mainZmc.set_check_ok_1()
            #         if ret != 0:
            #             self.keep_error_msg("控制器,设置视觉检测OK信号失败","mainZmc",self.boardIndex)
            #             return
            #
            #         self.send_image_to_web(imageRes,info) # 发送图片到前端的额界面
            #         save_ok_image(self.boardIndex,imageColor,self.widthPix,self.heightPix)
            #         save_ok_res_image(self.boardIndex,imageRes)
            #         self.update_order_info_to_web()  # 更新作业信息
            #         self.increase_board_index()
            #     else:
            #         self.send_image_to_web(imageRes,info) # 发送图片到前端的额界面
            #         msgVal = {
            #             "val": "totalBoardError",
            #             "hintInfo": "id_{} 物料检测未通过: {}".format(self.boardIndex, info),
            #         }
            #         send_produce_state_to_ui(msgVal)
            #         self.webModify = True  # 等待前端界面工人操作.
            #         while self.webModify:
            #             gevent.sleep(0.5)
            #             continue
            else:
                gevent.sleep(0.5)
                continue

    def web_request_re_get_image(self):
        """
        界面上点击了重新拍照
        :return:
        """
        # 关闭蜂鸣器报警
        self.mainZmc.close_buzzer_alarm()
        # 如果已经点击了板回退按钮,这个时候直接重拍就可以了.
        self.logger.debug("id_{} 收放板机: 界面上点击了重新拍照,进行重拍!".format(self.boardIndex))
        ret = 0
        if self.waitBoardBack:
            self.waitBoardBack = False
        else:
            ret = self.mainZmc.img_result_failed()
    
        self.webModify = False
        return "ok" if ret == 0 else ret

    def get_send_data_to_zmc(self,catchPos,angle):
        """
        将抓板位置和角度转换为发送给底层的格式.y坐标+230,角度加上角度补偿,然后乘以100.
        :return:
        """
        catchPosX,catchPosY = catchPos
        catchPosX = round(catchPosX,1)
        catchPosY = round(catchPosY,1)
        angleSend = round(cp.angleOffset - angle)
        return [catchPosX,catchPosY],angleSend


    def update_order_info_to_web(self):
        """
        更新工单信息
        :return:
        """
        data = db.get_order_by_key(self.key)
        amount = data.get("amount",0)
        workAmount = data.get("workAmount",0)
        workEnd = get_time_now_string_for_web()
        newData = {
            "key":self.key,
            "amount":amount + 1,
            "workAmount":workAmount + 1,
            "workEnd":workEnd,
        }
        db.web_request_save_order(newData)
        msgVal = {
            "val":self.key
        }
        send_order_state_to_ui(msgVal)



    def send_image_to_web(self,imageRes,info):
        """
        发送图片到前端界面
        :param imageRes:识别的结果图
        :return:
        """
        newWidth = 500
        newHeight = 500
        newImage = cv.resize(imageRes,(newWidth,newHeight),cv.INTER_AREA)
        msgVal = {
            "val":"boardImageState",
            "modalVisible":True,
            "boardImageSrc":image_to_string(newImage),
            "calcInfo":info
        }
        send_produce_state_to_ui(msgVal)


    def increase_board_index(self):
        self.boardIndex += 1
        if self.boardIndex > self.indexMax:
            self.boardIndex = 1
        newData = {
            "boardIndex": self.boardIndex
        }
        db.save_working_info_data(newData)
        # 做板数量加1
        db.update_produce_record(1)
        
    def set_carrier_Size(self):
        self.mainZmc.set_put_carrier_Size_to_zmc()

    def produce_listener(self):
        """
        自动作业监听线程函数,主要用来监控自动作业过程中的各个作业函数的任务.
        :return:
        """
        print("produce_listener start")
        self.logger.debug("in auto_listener ,自动作业监听线程开启.")
        self.set_carrier_Size()
        self.calcThread = threading.Thread(target=self.camera_listener, name="calc_img")
        self.calcThread.setDaemon(True)
        self.calcThread.start()
        while not self.deviceStates.workStopFlag:
            gevent.sleep(0.1)
        calcThread = self.calcThread
        gevent.sleep(1)
        if calcThread.is_alive():
            stop_thread(calcThread)
        self.clientSocket.close()
        self.logger.debug("检测到自动作业退出标志,强制结束各个作业线程")

    def prepare_produce_init(self, orderInfo):
        self.key = orderInfo["key"]
        data = db.get_working_info()[1]
        self.boardIndex = data.get("boardIndex")
        self.indexMax = cp.maxIndex
        self.widthMm = orderInfo["width"]
        self.heightMm = orderInfo["height"]
        self.cutId = orderInfo["cutId"]
        pinFlag = self.mainZmc.get_pin_flag()
        # self.jointNumber = orderInfo.get("jointNumber","4")
        if pinFlag:
            self.jointNumber = "4"
        else:
            self.jointNumber = "6"
        self.widthPix = int(self.widthMm * cp.mm2pix)
        self.heightPix = int(self.heightMm * cp.mm2pix)
        self.logger.debug("id_{}: 自动作业开始工单初始化: 物料号: {}, {}拼板,宽度: {},高度: {},开始作业板编号: {}".format(
            self.boardIndex,self.cutId,self.jointNumber,self.widthMm,self.heightMm,self.boardIndex))
        if self.jointNumber == "4":
            ret = self.mainZmc.set_4pin_flag()
            if ret != 0:
                return ret
        else:
            ret = self.mainZmc.set_6pin_flag()
            if ret != 0:
                return ret

    def web_request_close_produce_start_modal(self):
        """
        前端界面点击了关闭设备启动提示框.
        :return:
        """
        self.produceStartCheck = False
        msgVal = {
            "val": "produceStartModal",
            "modalVisible": False,
            "produceStartInfo": ""
        }
        send_produce_state_to_ui(msgVal)

    def web_request_start_produce(self, orderInfo):
        orderInfos,chooseToWhere = orderInfo
        if chooseToWhere == 1:
            self.mainZmc.set_put_board_to_LV1()
        else:
            self.mainZmc.set_put_board_to_LV2()
        try:
            produceListenerThread = self.produceListenerThread
            if produceListenerThread.is_alive():
                stop_thread(produceListenerThread)
                self.logger.debug("检测到线程已经开启,强制结束线程!")
        except Exception as e:
            errorInfo = str(e)
            print(errorInfo)

        self.prepare_produce_init(orderInfos)
        self.orderInfo = orderInfos
        ret = self.mainZmc.main_zmc_start()
        # 设备启动其实框
        msgVal = {
            "val": "produceStartModal",
            "modalVisible": True,
            "produceStartInfo": "设备启动中..."
        }
        send_produce_state_to_ui(msgVal)
        gevent.sleep(0.5)
        self.produceStartCheck = True
        while self.produceStartCheck:
            _,val =  self.mainZmc.is_running()
            info = "设备启动中..." if val == 0 else "设备启动成功!"
            if val == 1:
                self.produceStartCheck = False
                msgVal = {
                     "val": "produceStartModal",
                    "modalVisible": False,
                    "produceStartInfo": ""
                }
            else:
                msgVal = {
                    "val": "produceStartModal",
                    "modalVisible": True,
                    "produceStartInfo": info
                }
            send_produce_state_to_ui(msgVal)
            gevent.sleep(0.4)
        # 如果循环退出来了,再检测一下设备是否启动成功,如果启动成功了,就往下走,如果启动失败了,就提示.
        _, val = self.mainZmc.is_running()
        if val == 0:
            return "启动失败,检测到社保启动的标志位[modbus_bit(100)] = 0"
        else:
            msgVal = {
                "val": "produceStartModal",
                "modalVisible": False,
                "produceStartInfo": ""
            }
            send_produce_state_to_ui(msgVal)
        self.deviceStates.workStopFlag = False
        if ret != 0:
            self.keep_error_msg(ret, "mainZmc", self.boardIndex)
            return "设备启动失败，请检查电源线和网线"
        self.logger.debug('界面上点击了自动作业启动')
        self.clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 开启保活机制
        self.clientSocket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, True)
        self.clientSocket.ioctl(socket.SIO_KEEPALIVE_VALS, (1, 120 * 1000, 30 * 1000))
        # ret = self.clientSocket.connect(addr)
        # print("ret: {}".format(ret))
        # res = self.clientSocket.recv(1024)
        # print("res: {}".format(res))
        self.produceListenerThread = threading.Thread(target=self.produce_listener, name="produceListenerThread")
        self.produceListenerThread.setDaemon(True)
        self.produceListenerThread.start()
        return "ok"

    def web_request_stop_produce(self):
        """
        界面上点击了停止按钮
        :return:
        """
        self.deviceStates.workStopFlag = True
        self.logger.debug("id_{} 界面上点击了停止自动作业".format(self.boardIndex))
        self.mainZmc.main_zmc_stop()
        return "ok"

    def web_request_reset_all(self):
        """
        界面上点击了复位,复位的事情做完
        :return:
        """
        self.logger.debug("id_{} 界面上点击了复位按钮,开始复位".format(self.boardIndex))
        msgVal = {
            "val":"resetAllModal",
            "modalVisible": True,
            "resetInfo": "设备复位中..."
        }
        ret  = self.mainZmc.cam_zmc_reset()
        if ret != 0:
            self.keep_error_msg(ret,"mainZmc",self.boardIndex)
            return ret
        send_produce_state_to_ui(msgVal)
        time.sleep(0.3)
        self.resetCheck = True
        while self.resetCheck:
            ret, val = self.mainZmc.is_reset_state()
            info = "1. 设备复位中..." if val == 1 else "1. 设备复位完成!"
            if val == 0:  # 全部是复位完成状态,就退出
                self.resetCheck = False
                msgVal = {
                    "val": "resetAllModal",
                    "modalVisible": False,
                    "resetInfo": "设备复位完成..."
                }
                send_produce_state_to_ui(msgVal)
            else:
                msgVal = {
                    "val": "resetAllModal",
                    "modalVisible": True,
                    "resetInfo": info
                }
                send_produce_state_to_ui(msgVal)
            gevent.sleep(0.2)
        msgVal = {
            "val": "resetAllModal",
            "modalVisible": False,
            "resetInfo": "设备复位完成..."
        }
        send_produce_state_to_ui(msgVal)





    def web_request_save_flat_height_max(self,thicknessMax):
        """
        设置放板厚度
        :param thicknessMax:
        :return:
        """
        try:
            if int(thicknessMax) > 280:
                thicknessMax = 280
            else:
                thicknessMax = int(thicknessMax)

            ret = self.mainZmc.set_flat_board_max(thicknessMax)
            if ret != 0:
                self.keep_error_msg(ret,"mainZmc",self.boardIndex)
                return ret
            newData = {
                "boardThicknessMax":thicknessMax
            }
            db.save_working_info_data(newData)
            return "ok"
        except Exception as e:
            errorInfo = str(e)
            return "error",errorInfo

    def web_request_save_get_image(self):
        self.mainZmc.set_output(6, 1)
        gevent.sleep(1)
        self.camera.start_grabbing()
        res, image = self.camera.get_image()
        if res != 0:
            for i in range(3):
                res,image = self.camera.get_image()
                if res == "ok":
                    break
            else:
                self.keep_error_msg("id_{} 相机拍照失败三次,请检查电源线和网线!","mainZmc",self.boardIndex)
                return
        image = get_image_used(image)
        self.mainZmc.set_output(6, 0)
        save_demarcate_image(image)
    
    def web_request_open_image_location(self):
        ret = os.system("explorer.exe {}".format(RAW_DEMARCATE_DIR))
        return "ok" if ret == 1 else ret

    def web_request_set_Lv1_full_quit(self):
        self.logger.debug("界面点击下降一号收料平台！")
        return self.mainZmc.set_Lv1_full_quit()

    def web_request_set_Lv2_full_quit(self):
        self.logger.debug("界面点击下降二号收料平台！")
        return self.mainZmc.set_Lv2_full_quit()

    def web_request_clear_grate_alarm(self):
        return self.mainZmc.clear_grate_alarm()

    def web_request_set_place_start(self, place_start):
        if not place_start:
            ret =self.mainZmc.set_4pin_flag()
            self.jointNumber = "4"
            return ret
        else:
            ret =self.mainZmc.set_6pin_flag()
            self.jointNumber = "6"
            return ret
    
    def web_request_set_AGV_or_artificial(self, place_start):
        if place_start:
            ret = self.mainZmc.set_modbus_bit(70, 0)
            if ret != 0:
                ret = self.mainZmc.set_modbus_bit(70, 0)
                if ret != 0:
                    self.err_msg = "in re_get_pic() set web_request_set_AGV_or_artificial bit(21) failed!"
                    return self.err_msg
            self.logger.debug("界面员工选择由AGV模式变更为手动模式!")
            self.mainZmc.set_modbus_bit(34, 0)
            self.mainZmc.set_modbus_bit(35, 0)
        else:
            ret = self.mainZmc.set_modbus_bit(70, 1)
            if ret != 0:
                ret = self.mainZmc.set_modbus_bit(70, 1)
                if ret != 0:
                    self.err_msg = "in re_get_pic() set web_request_set_AGV_or_artificial bit(21) failed!"
                    return self.err_msg
            self.logger.debug("界面员工选择由手动模式变更为AGV模式!")
        return "ok"

    def web_request_close_raster(self):
        ret = self.mainZmc.set_modbus_bit(201, 1)
        if ret != 0:
            ret = self.mainZmc.set_modbus_bit(201, 1)
            if ret != 0:
                self.err_msg = "in re_get_pic() set web_request_close_raster bit(201) failed!"
                return self.err_msg
        ret = self.mainZmc.set_modbus_bit(202, 1)
        if ret != 0:
            ret = self.mainZmc.set_modbus_bit(202, 1)
            if ret != 0:
                self.err_msg = "in re_get_pic() set web_request_close_raster bit(202) failed!"
                return self.err_msg
        return 0

    def web_request_open_raster(self):
        ret = self.mainZmc.set_modbus_bit(201, 0)
        if ret != 0:
            ret = self.mainZmc.set_modbus_bit(201, 0)
            if ret != 0:
                self.err_msg = "in re_get_pic() set web_request_close_raster bit(201) failed!"
                return self.err_msg
        ret = self.mainZmc.set_modbus_bit(202, 0)
        if ret != 0:
            ret = self.mainZmc.set_modbus_bit(202, 0)
            if ret != 0:
                self.err_msg = "in re_get_pic() set web_request_close_raster bit(202) failed!"
                return self.err_msg
        return 0

